<?php

namespace App\Services\Easywechat;

use App\Master\Enum\RedisKeyEnum;
use App\Master\Framework\Extend\Module;
use App\Utils\RedisUtil;
use EasyWeChat\MiniApp\Application;
use Illuminate\Support\Facades\Cache;

/**
 * 微信小程序开发组
 * class MiniAppService
 */
class MiniAppService extends EasyModule
{
    /**
     * @var Application
     */
    private Application $app;
    private array       $config;

    /**
     * 实例化
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     */
    public function __construct()
    {
        parent::__construct();
        // 微信开发配置
        $_table = "WechatConfig";
        $config = Cache::remember("MINI_APP_SERVICE_{$_table}", $this->ttl, function () use ($_table) {
            $module = Module::_SetupModule($_table);
            return $module['mini'] ?? [];
        });

        $config = [
            'app_id'  => $config['app_id'] ?? '',
            'secret'  => $config['app_secret'] ?? '',
            'token'   => $config['token'] ?? '',
            'aes_key' => $config['aes_key'] ?? '',
            /**
             * 接口请求相关配置，超时时间等，具体可用参数请参考：
             * https://github.com/symfony/symfony/blob/5.3/src/Symfony/Contracts/HttpClient/HttpClientInterface.php
             */
            'http'    => [
                'throw'   => true, // 状态码非 200、300 时是否抛出异常，默认为开启
                'timeout' => 5.0,
                'retry'   => true, // 使用默认重试配置
            ],
        ];

        $this->config = $config;
    }

    /**
     * openid 授权
     *
     * @param string $code
     * @return bool
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    public function jscode2session(string $code): bool
    {
        $app = $this->app;

        $api = $app->getClient();

        $response = $api->get('/sns/jscode2session', [
            'appid'      => $this->config['app_id'],
            'secret'     => $this->config['secret'],
            'js_code'    => $code,
            'grant_type' => 'authorization_code',
        ])->throw(false);

        if (!$this->response($response)) {
            return false;
        }

        return true;
    }

    /**
     * 获取手机号
     * @param string $code
     * @return bool
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    public function getUserPhone(string $code): bool
    {
        $app = $this->app;

        if (!$access_token = $this->getAccessToken()) {
            return false;
        }

        $api = $app->getClient();

        $response = $api->postJson("/wxa/business/getuserphonenumber?access_token={$access_token}", [
            'code' => $code
        ]);

        if (!$this->response($response)) {
            return false;
        }

        return true;
    }

    /**
     * 获取 access_token
     * @return false|mixed
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     * @throws \Exception
     */
    private function getAccessToken()
    {
        if ($access_token = RedisUtil::getInstance(RedisKeyEnum::WX_EASY_WECHAT_ACCESS_TOKEN)->get()) {
            return $access_token;
        }

        if (!$this->stable_token()) {
            return false;
        }

        $data = $this->get();
        if (empty($data['access_token'])) {
            return false;
        }

        RedisUtil::getInstance(RedisKeyEnum::WX_EASY_WECHAT_ACCESS_TOKEN)->setex($data['access_token'], (int)($data['expires_in'] ?? 0));

        return $data['access_token'];
    }

    /**
     * @return bool
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    private function stable_token(): bool
    {
        $app = $this->app;

        $api = $app->getClient();

        $response = $api->postJson('/cgi-bin/stable_token', [
            'grant_type'    => 'client_credential',
            'appid'         => $this->config['app_id'],
            'secret'        => $this->config['secret'],
            'force_refresh' => false,// 默认false:普通模式false;强制刷新模式true;
        ]);

        if (!$this->response($response)) {
            return false;
        }

        return true;
    }
}